﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ArithCollect {
    public class Expr : BottomChain<Expr.ExprNode> {
        /// <summary>
        /// 依然是6位基元运算
        /// </summary>
        internal enum Op {
            plus, subtract, multiply, division, power, log
        }
        public class ExprNode {
            public int seqence;
            internal Op op; public double value;
        }
        public delegate double Fy(params double[] ds);
        Dictionary<string, Fy> funcs;
        protected Expr() {
            this.exprString = string.Empty;
            funcs = new Dictionary<string, Fy>();
            funcs.Add("sin", x => Math.Sin(x[0]));
            funcs.Add("cos", x => Math.Cos(x[0]));
            funcs.Add("tan", x => Math.Tan(x[0]));
            funcs.Add("asin", x => Math.Asin(x[0]));
            funcs.Add("acos", x => Math.Acos(x[0]));
            funcs.Add("atan", x => Math.Atan(x[0]));
            funcs.Add("abs", x => Math.Abs(x[0]));
            funcs.Add("sum", x => {
                var r = 0d;
                foreach (var item in x) {
                    r += item;
                }
                return r;
            });
            funcs.Add("avg", x => {
                var r = 0d;
                foreach (var item in x) {
                    r += item;
                }
                return r / x.Length;
            });
        }
        /// <summary>
        /// 注册以后请调用 ToExpr方法，若没有任何可注册的自定义函数则不必调用本方法也不必手动调用 ToExpr方法。
        /// </summary>
        /// <param name="name">自定义函数名称</param>
        /// <param name="func">自定义函数</param>
        /// <returns></returns>
        public bool RegistryFunc(string name, Fy func) {
            var r = true;
            if (!funcs.ContainsKey(name))
                funcs.Add(name, func);
            else r = false;
            return r;
        }
        string ParseFuncs() {
            var sample = this.exprString;
            var pattern0 = @"\(([\d,\s]*\.?[\d,\s]*)*\,*\)";
            var parameters = new List<double>();
            foreach (var item in funcs) {
                var pattern1 = item.Key + pattern0;
                var x = System.Text.RegularExpressions.Regex.Match(sample, pattern1);
                while (x.Success) {
                    var x3 = x.Value.Split(new[] { item.Key, "(", ")", "," }, StringSplitOptions.RemoveEmptyEntries);
                    parameters.Clear();
                    foreach (var item2 in x3) {
                        parameters.Add(double.Parse(item2));
                    }
                    var x2 = item.Value.Invoke(parameters.ToArray());
                    var x4 = x2.ToString("F16");
                    if (x2 < 0) {
                        x4 = string.Format("({0})", x4);
                    }
                    sample = sample.Remove(x.Index, x.Length);
                    sample = sample.Insert(x.Index, x4);
                    x = System.Text.RegularExpressions.Regex.Match(sample, pattern1);
                }
            }
            return sample;
        }
        public static implicit operator double(Expr s) {
            try {
                s.Merge();
                return s.root.Data.value;
            } catch {
                s.Clear(ref s.root);
                return double.NaN;
            }
        }
        public override string ToString() {
            return exprString;
        }
        string exprString;
        string parseString;//解析式
        public static implicit operator Expr(string s) {
            var r = new Expr();
            r.exprString = s;
            var k = r.exprString.Replace(" ","");
            var funcPattern = @"[a-z,A-Z]+\([\d,\*,\+,\-,\/,\^\&,\.,\(,\)]*\)";
            var x = System.Text.RegularExpressions.Regex.Match(k, funcPattern);
            while (x.Success) {
                var x2 = x.Value.Split(new[] { '(', ')' }, StringSplitOptions.RemoveEmptyEntries)[0];//x2是函数名
                if (!r.funcs.ContainsKey(x2)) {
                    return r;//如果发现系统未定义的自定义函数那么就跳出，之后请注册这些未知的自定义函数，然后手动调用ToExpr方法
                }
                x = x.NextMatch();
            } r.ToExpr();
            return r;
        }
        /// <summary>
        /// 做一个特殊化的处理，旨在把计算括号之间的运算做协调形入)[+,-,*,/,^,&](==>)^1[+,-,*,/,^,&](
        /// </summary>
        /// <param name="k"></param>
        /// <returns></returns>
        string CreateCusp(string k) {
            var r = k;
            var pattern = @"\)\s*[\*,\/,\+,\-,\^,\&]\s*\(";
            var x = System.Text.RegularExpressions.Regex.Match(r, pattern);
            while (x.Success) {
                r = r.Replace(x.Value, x.Value.Insert(1, "^1"));
                x = System.Text.RegularExpressions.Regex.Match(r, pattern);
            }
            return r;
        }
        public void ToExpr() {
            var s = this.ParseFuncs();
            var k = CreateCusp(s);
            if (k.Trim()[k.Length - 1] == ')') k += "+0";
            if (k.Trim()[0] == '(') k = string.Format("1*{0}", k);
            k = k.Replace(" ", "");
            var seq = 0;
            var cs = new char[] { '+', '-', '*', '/', '^', '&', '(', ')', ' ' };
            var ss = k.Split(cs, StringSplitOptions.RemoveEmptyEntries);
            this.Clear(ref this.root);
            foreach (var item in ss) {
                var sn = new Expr.ExprNode();
                sn.value = double.Parse(item);
                var index = k.IndexOf(item);
                var x = k.Substring(0, index);
                if (System.Text.RegularExpressions.Regex.Match(x, @"\(\-").Success) {
                    sn.value *= -1;
                }
                for (int j = index - 1; j >= 0; j--) {
                    switch (k[j]) {
                        case '+': sn.op = Op.plus; goto default;
                        case '-': sn.op = Op.subtract; goto default;
                        case '*': sn.op = Op.multiply; goto default;
                        case '/': sn.op = Op.division; goto default;

                        case '^': sn.op = Op.power; goto default;
                        case '&': sn.op = Op.log; goto default;
                        case '(': sn.seqence = ++seq; continue;
                        case ')': sn.seqence = --seq; continue;
                        default:
                            sn.seqence = seq;
                            break;
                    }
                }
                k = k.Substring(index + item.Length);
                this.Add(sn);
            }
        }
        void Merge() {
            while (this.Count != 1) {
                Merge(this.root);
            }
        }
        void Merge(BottomChain<ExprNode>.Node node) {
            var x1 = node.next;
            if (x1 != null) {
                var x2 = x1.next;
                if (x2 != null) {
                    if (node.Data.seqence < x1.Data.seqence && x1.Data.seqence > x2.Data.seqence) {
                        x1.Data.seqence--;//去单括号
                        return;
                    } else if (node.Data.seqence == x1.Data.seqence && x1.Data.seqence > x2.Data.seqence) {
                        ComputeDriectLeft(node.Data, x1.Data);
                        this.Remove(x1);
                    } else if (node.Data.seqence == x1.Data.seqence && x1.Data.seqence == x2.Data.seqence) {
                        if (GetOpValue(x1.Data.op) >= GetOpValue(x2.Data.op)) {
                            ComputeDriectLeft(node.Data, x1.Data);
                            this.Remove(x1);
                        } else {
                            Merge(x1);
                        }
                    } else {
                        Merge(x1);
                    }
                } else if (this.Count <= 3) {
                    ComputeDriectLeft(node.Data, x1.Data);
                    this.Remove(x1);
                }
            }
        }
        ExprNode GetPreviousNode(ExprNode n) {
            var np = default(ExprNode);
            foreach (var item in this.QueryNode()) {
                if (item.next != null && item.next.Data == n) {
                    np = item.Data; break;
                }
            }
            return np;
        }
        bool GetNodeOpEffect(ExprNode n) {
            var r = false;
            if (n.op == Op.subtract) {
                var n2 = this.GetPreviousNode(n);
                if (n2 != null && n2.seqence == n.seqence) {
                    r = true;
                }
            }
            return r;
        }
        void ComputeDriectLeft(ExprNode left, ExprNode right) {
            //向左计算
            switch (right.op) {
                case Op.plus:
                    if (GetNodeOpEffect(left)) {
                        left.value *= -1;
                        left.value += right.value;
                        left.value = Math.Abs(left.value);
                    } else {
                        left.value += right.value;
                    }
                    break;
                case Op.subtract:
                    if (GetNodeOpEffect(left)) {
                        left.value *= -1;
                        left.value += right.value;
                        left.value = Math.Abs(left.value);
                    }
                    left.value -= right.value;
                    break;
                case Op.multiply:
                    left.value *= right.value;
                    break;
                case Op.division:
                    left.value /= right.value;
                    break;
                case Op.power:
                    left.value = Math.Pow(left.value, right.value);
                    break;
                case Op.log:
                    left.value = Math.Log(left.value, right.value);
                    break;
                default:
                    break;
            }
        }
        int GetOpValue(Op op) {
            var r = 0;
            switch (op) {
                case Op.plus:
                    r = 0;
                    break;
                case Op.subtract:
                    r = 0;
                    break;
                case Op.multiply:
                    r = 1;
                    break;
                case Op.division:
                    r = 1;
                    break;
                case Op.power:
                    r = 2;
                    break;
                case Op.log:
                    r = 2;
                    break;
                default:
                    break;
            }
            return r;
        }
        //
        Expr GetRootCopy(BottomChain<Expr.ExprNode> n) {
            var x = new Expr();
            foreach (var item in n.Query()) {
                x.Add(new ExprNode { op = item.op, seqence = item.seqence, value = item.value });
            }
            this.Count = x.Count;
            return x;
        }
        public double[] F(int index, params double[] xs) {
            var r = new List<double>();
            if (this.Count <= 1 && !string.IsNullOrEmpty(this.exprString)) {
                ToExpr();
            } else {
                var x = GetRootCopy(this);
                foreach (var item in xs) {
                    var x2 = GetRootCopy(x);
                    this[index].value = item;
                    r.Add(this);
                    this.root = x2.root;
                }
            }
            return r.ToArray();
        }
        public double[] F(params Tople<int, double[]>[] xs) {
            if (xs == null) {
                return null;
            }
            var gp = from item in xs group item by item.Item2.Length;
            if (gp.Count() > 1) {
                throw new Exception("多元函数所有变量组的值个数必须相等。");
            }
            var len = xs[0].Item2.Length;
            var r = new List<double>();
            if (this.Count <= 1 && !string.IsNullOrEmpty(this.exprString)) {
                ToExpr();
            } else {
                var x = GetRootCopy(this);
                for (int i = 0; i < len; i++) {
                    var x2 = GetRootCopy(x);
                    foreach (var item in xs) {
                        this[item.Item1].value = item.Item2[i];
                    }
                    r.Add(this);
                    this.root = x2.root;
                }
            }
            return r.ToArray();
        }
    }
}
